package com.atguigu.case2;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;


import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class DistributedLock {
    private String connectString="192.168.80.130:2181,192.168.80.131:2181,192.168.80.132:2181";
    private Integer sessionTimeout=2000;
    private ZooKeeper zk;
    private CountDownLatch connectLatch=new CountDownLatch(1);
    private CountDownLatch waitLatch=new CountDownLatch(1);
    private String waitPath;
    private String currentNode;

    public DistributedLock() throws Exception {
        //获取连接
        zk=new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                //connectLatch:如果连接上zk,可以释放
                if(watchedEvent.getState()==Event.KeeperState.SyncConnected){
                    //如果状态是“连接”
                    connectLatch.countDown();//释放掉
                }
                //waitLatch需要释放
                if(watchedEvent.getType()==Event.EventType.NodeDeleted&&watchedEvent.getPath().equals(waitPath)){
                    //如果节点是删除状态并且和上一个节点相同
                    waitLatch.countDown();
                }
            }
        });
        connectLatch.await();//等待zk正常连接后(前一个进程)，才往下走；
        //判断根节点“/lock”是否存在
        Stat stat = zk.exists("/locks", false);
        if(stat==null){
            //创建一个根节点
            zk.create("/locks","lock".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
        }
    }
    //对zk加锁
    public void zklock() throws Exception{
        //创建对应的临时带序号节点
        currentNode = zk.create("/locks/seq-",null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        //判断创建的节点是否为最小的序号节点。如果是，获取锁。如果不是，监听他序号前一个节点；
        List<String> children = zk.getChildren("/locks", false);
        //如果children只有一个值，那就直接获取值，如果不是，就需要判断谁的值最小
        if(children.size()<=1){
            return;
        }else {
            Collections.sort(children);
            //获取节点名称：seq-xxxx
            String thisNode = currentNode.substring("/locks/".length());
            int i = children.indexOf(thisNode);
            if(i==-1){
                System.out.println("数据异常");
            }else if(i==0){
                return;
            }else {
                //需要监听前一个结点
                waitPath="/locks/"+children.get(i-1);
                zk.getData(waitPath,true,new Stat());
                //等待监听
                waitLatch.await();
                return;
            }
        }
    }
    //对zk解锁
    public void unzklock() throws Exception{
        //删除节点
        zk.delete(currentNode,-1);
    }
}
